#pragma once 
#include <vector>
#include <functional>
#include <unordered_map>
#include <sys/select.h>
#include "tcp_socket.hpp"
#include <iostream>
//主函数和部分头文件，在_dict/tcp_dict中

//for DeBug
inline void PrintFdSet(fd_set* fds, int max_fd)
{
    printf("select fds : ");
    for(int i = 0; i < max_fd; i++){
        if(!FD_ISSET(i, fds)){
            continue;
        }
        printf("%d", i);
    }
    std::cout << std::endl;
}

typedef std::function<void(const std::string& req, std::string* resp)> Handler;//主函数调用的业务处理需要作为服务器的参数，以便服务器对其处理

// 把 Select 封装成一个类. 这个类虽然保存很多 TcpSocket 对象指针, 但是不管理内存
class Selector{
public: 
    Selector()//初始化init
    {
        max_fd_ = 0;
        FD_ZERO(&read_fds_);
    }
    bool Add(const TcpSocket& sock){
        int fd = sock.Getfd();
        printf("[Selector::Add] %d\n", fd);
        if(fd_map_.find(fd) != fd_map_.end()){
            printf("Add failed, fd has in Selector~\n");
            return false;
        }
        fd_map_[fd] = sock;
        FD_SET(fd, &read_fds_);
        if(fd > max_fd_){
            max_fd_ = fd;
        }
        return true;
    }
    bool Del(const TcpSocket& sock){
        int fd = sock.Getfd();
        if(fd_map_.find(fd) == fd_map_.end()){
            printf("Del failed, fd has not in Selector\n");
            return false;
        }
        FD_CLR(fd, &read_fds_);
        fd_map_.erase(fd);
        for(int i = max_fd_; i >= 0; i--){
            if(!FD_ISSET(i, &read_fds_)){
                continue;
            }
            max_fd_ = i;
            break;
        }
        return true;
    }

    // 返回读就绪的文件描述符集
    bool Wait(std::vector<TcpSocket>* output){//output储存的是满足可读条件的Socket,对read_fds_遍历找出可读的，然后放进output
        output->clear();
        fd_set tmp = read_fds_;
        PrintFdSet(&tmp, max_fd_);
        int ret = select(max_fd_+1, &tmp, NULL, NULL, NULL);
        if(ret < 0) 
        {
            perror("select");
            return false;
        }
        for(int i = 0; i < max_fd_ + 1; i++){
            if(!FD_ISSET(i, &tmp)){
                continue;
            }
            output->push_back(fd_map_[i]);
        }
        return true;
    }
private:
    int max_fd_;
    fd_set read_fds_;
    // 文件描述符和 socket 对象的映射关系
    std::unordered_map<int, TcpSocket> fd_map_;//存放int - TcpSocket是为获取其fd，以便对其Socket读写；
};


class Select_Tcpserver{
public:
    Select_Tcpserver(const std::string& ip, uint16_t port):_ip(ip), _port(port)
    {}
    bool Start(Handler handler)const{
        //1.创建服务器监听Socket
        TcpSocket listen_sock;//原本可作为服务器的私有属性
        bool ret = listen_sock.Socket();
        if(!ret){
            return false;
        }
        //绑定端口号
        ret = listen_sock.Bind(_ip, _port);
        if(!ret){
            return false;
        } 
        //监听
        ret = listen_sock.Listen(5);
        if(!ret){
            return false;
        } 
        //4.创建Selector对象
        Selector selector;
        selector.Add(listen_sock);
        //进入事件循环
        for(;;){
            std::vector<TcpSocket> output;
            bool ret = selector.Wait(&output);
            if(!ret){
            return false;
            }
            //6.根据文件描述符的差距，决定后续的处理逻辑
            for(ssize_t i = 0; i < output.size(); i++){
                if(output[i].Getfd() == listen_sock.Getfd()){
                    // 如果就绪的文件描述符是 listen_sock, 就执行 accept, 并加入到 select 中
                    TcpSocket new_sock;//创建新的sockfd进行交互
                    listen_sock.Accept(&new_sock, NULL, NULL);
                    selector.Add(new_sock);
                }else{
                    // 如果就绪的文件描述符是 new_sock, 就进行一次请求的处理
                    std::string rep, resp;
                    bool ret = output[i].Recv(&rep);//读取用户需求失败，需关闭对此用户的连接，不需要return，不能让一颗老鼠shi坏了整锅粥
                    if(!ret){
                        // [注意!] 需要关闭 socket
                        selector.Del(output[i]);
                        output[i].Close();
                        continue;
                    }
                    //调用业务处理函数
                    handler(rep, &resp);
                    //处理完成，发送会客户端所需的结果
                    output[i].Send(resp);
                }
            } 
        }
        return true;
    }
private:
    std::string _ip;
    uint16_t _port;
};